<?php

namespace App\Services;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

class SecurityService
{
    /**
     * 清理和验证输入数据
     */
    public static function sanitizeInput(array $data): array
    {
        $sanitized = [];
        
        foreach ($data as $key => $value) {
            if (is_string($value)) {
                // 移除潜在的恶意字符
                $value = self::removeMaliciousContent($value);
                
                // 限制长度
                $value = self::limitLength($value, $key);
                
                // 转义特殊字符
                $value = self::escapeSpecialChars($value, $key);
            } elseif (is_array($value)) {
                $value = self::sanitizeInput($value);
            }
            
            $sanitized[$key] = $value;
        }
        
        return $sanitized;
    }

    /**
     * 移除恶意内容
     */
    protected static function removeMaliciousContent(string $input): string
    {
        // 移除脚本标签
        $input = preg_replace('/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/mi', '', $input);
        
        // 移除事件处理器
        $input = preg_replace('/\s*on\w+\s*=\s*["\'][^"\']*["\']/i', '', $input);
        
        // 移除javascript:协议
        $input = preg_replace('/javascript:/i', '', $input);
        
        // 移除data:协议（除了图片）
        $input = preg_replace('/data:(?!image\/)/i', '', $input);
        
        // 移除潜在的SQL注入字符
        $input = str_replace(['--', '/*', '*/', 'xp_', 'sp_'], '', $input);
        
        return $input;
    }

    /**
     * 限制输入长度
     */
    protected static function limitLength(string $input, string $field): string
    {
        $limits = [
            'name' => 100,
            'email' => 255,
            'phone' => 20,
            'title' => 200,
            'description' => 2000,
            'content' => 10000,
            'address' => 500,
            'notes' => 1000,
            'reason' => 500,
            'admin_comment' => 1000,
            'keyword' => 100,
            'query' => 200,
        ];

        $limit = $limits[$field] ?? 1000;
        
        if (mb_strlen($input) > $limit) {
            Log::warning('Input length exceeded limit', [
                'field' => $field,
                'length' => mb_strlen($input),
                'limit' => $limit,
                'user_id' => auth()->id(),
            ]);
            
            return mb_substr($input, 0, $limit);
        }

        return $input;
    }

    /**
     * 转义特殊字符
     */
    protected static function escapeSpecialChars(string $input, string $field): string
    {
        // 对于HTML内容，使用htmlspecialchars
        if (in_array($field, ['description', 'content', 'notes', 'admin_comment'])) {
            return htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
        }

        // 对于其他字段，只转义引号
        return str_replace(['"', "'"], ['&quot;', '&#39;'], $input);
    }

    /**
     * 验证文件上传
     */
    public static function validateFileUpload($file, array $allowedTypes = [], int $maxSize = 5242880): array
    {
        $errors = [];

        if (!$file) {
            $errors[] = '请选择要上传的文件';
            return $errors;
        }

        // 检查文件大小
        if ($file->getSize() > $maxSize) {
            $errors[] = '文件大小不能超过 ' . self::formatBytes($maxSize);
        }

        // 检查文件类型
        if (!empty($allowedTypes)) {
            $mimeType = $file->getMimeType();
            if (!in_array($mimeType, $allowedTypes)) {
                $errors[] = '文件类型不支持，允许的类型：' . implode(', ', $allowedTypes);
            }
        }

        // 检查文件扩展名
        $extension = strtolower($file->getClientOriginalExtension());
        $dangerousExtensions = ['php', 'phtml', 'php3', 'php4', 'php5', 'php7', 'pht', 'phtm', 'jsp', 'asp', 'aspx'];
        
        if (in_array($extension, $dangerousExtensions)) {
            $errors[] = '不允许上传此类型的文件';
        }

        // 检查文件名
        $filename = $file->getClientOriginalName();
        if (preg_match('/[<>:"|?*]/', $filename)) {
            $errors[] = '文件名包含非法字符';
        }

        return $errors;
    }

    /**
     * 格式化字节数
     */
    protected static function formatBytes(int $bytes): string
    {
        $units = ['B', 'KB', 'MB', 'GB'];
        $bytes = max($bytes, 0);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);
        
        $bytes /= pow(1024, $pow);
        
        return round($bytes, 2) . ' ' . $units[$pow];
    }

    /**
     * 生成安全的随机字符串
     */
    public static function generateSecureToken(int $length = 32): string
    {
        return Str::random($length);
    }

    /**
     * 验证CSRF令牌
     */
    public static function validateCsrfToken(Request $request): bool
    {
        $token = $request->header('X-CSRF-TOKEN') ?: $request->input('_token');
        
        if (!$token) {
            return false;
        }

        return hash_equals(session()->token(), $token);
    }

    /**
     * 记录安全事件
     */
    public static function logSecurityEvent(string $event, array $context = []): void
    {
        $context = array_merge($context, [
            'event' => $event,
            'ip' => request()->ip(),
            'user_agent' => request()->userAgent(),
            'user_id' => auth()->id(),
            'timestamp' => now()->toISOString(),
        ]);

        Log::warning('Security event detected', $context);
    }

    /**
     * 检查请求频率限制
     */
    public static function checkRateLimit(string $key, int $maxAttempts = 60, int $decayMinutes = 1): bool
    {
        $key = 'rate_limit:' . $key . ':' . request()->ip();
        
        $attempts = cache()->get($key, 0);
        
        if ($attempts >= $maxAttempts) {
            self::logSecurityEvent('Rate limit exceeded', [
                'key' => $key,
                'attempts' => $attempts,
                'max_attempts' => $maxAttempts,
            ]);
            
            return false;
        }

        cache()->put($key, $attempts + 1, now()->addMinutes($decayMinutes));
        
        return true;
    }

    /**
     * 验证输入格式
     */
    public static function validateInputFormat(string $input, string $type): bool
    {
        return match($type) {
            'email' => filter_var($input, FILTER_VALIDATE_EMAIL) !== false,
            'phone' => preg_match('/^1[3-9]\d{9}$/', $input),
            'url' => filter_var($input, FILTER_VALIDATE_URL) !== false,
            'ip' => filter_var($input, FILTER_VALIDATE_IP) !== false,
            'int' => filter_var($input, FILTER_VALIDATE_INT) !== false,
            'float' => filter_var($input, FILTER_VALIDATE_FLOAT) !== false,
            'date' => strtotime($input) !== false,
            'json' => json_decode($input) !== null,
            default => true,
        };
    }

    /**
     * 清理和验证搜索查询
     */
    public static function sanitizeSearchQuery(string $query): string
    {
        // 移除特殊字符
        $query = preg_replace('/[^\p{L}\p{N}\s\-_]/u', '', $query);
        
        // 限制长度
        $query = mb_substr($query, 0, 100);
        
        // 移除多余空格
        $query = preg_replace('/\s+/', ' ', trim($query));
        
        return $query;
    }

    /**
     * 检查是否为可疑请求
     */
    public static function isSuspiciousRequest(Request $request): bool
    {
        $suspiciousPatterns = [
            '/union\s+select/i',
            '/drop\s+table/i',
            '/delete\s+from/i',
            '/insert\s+into/i',
            '/update\s+set/i',
            '/script\s*>/i',
            '/javascript:/i',
            '/vbscript:/i',
            '/onload\s*=/i',
            '/onerror\s*=/i',
        ];

        $input = $request->getContent();
        
        foreach ($suspiciousPatterns as $pattern) {
            if (preg_match($pattern, $input)) {
                self::logSecurityEvent('Suspicious request detected', [
                    'pattern' => $pattern,
                    'input' => substr($input, 0, 500),
                ]);
                
                return true;
            }
        }

        return false;
    }
}
